package com.xy.tool.concurrent.locks.rdb;

import com.xy.lang.util.OpUtils;
import com.xy.tool.concurrent.locks.DisLock;
import com.xy.tool.concurrent.locks.rdb.dao.DisLockDao;
import com.xy.tool.concurrent.locks.rdb.domain.po.DisLockPO;
import com.xy.tool.context.UserContextHolder;
import org.springframework.dao.DuplicateKeyException;

import java.time.LocalDateTime;
import java.util.Objects;

public class RdbDisLock implements DisLock {

    private final DisLockDao disLockDao;

    private Long disLockId;
    private long lockExpireSeconds = 60;
    private long tryGetLockIntervalMillis = 10;

    public RdbDisLock(DisLockDao disLockDao) {
        Objects.requireNonNull(disLockDao);
        this.disLockDao = disLockDao;
    }

    @Override
    public boolean lock(String resource) {
        Objects.requireNonNull(resource);

        Long tenantId = (tenantId = UserContextHolder.getTenantId()) == null ? 0L : tenantId;

        DisLockPO disLockPO = new DisLockPO();
        disLockPO.setResource(resource);
        disLockPO.setTenantId(tenantId);
        disLockPO.setExpireTime(LocalDateTime.now().plusSeconds(lockExpireSeconds));
        for(int loop = 0; loop < 3; loop++) {
            try {
                disLockDao.saveDisLock(disLockPO);
                disLockId = disLockPO.getDisLockId();
                return true;
            } catch (DuplicateKeyException e) {
                DisLockPO found = disLockDao.getDisLock(tenantId, resource);
                if (found != null) {
                    if(LocalDateTime.now().isAfter(found.getExpireTime())) {
                        disLockDao.deleteDisLock(found.getDisLockId());
                    }
                }
            }
        }

        return false;
    }

    @Override
    public boolean lock(String resource, long timeoutMillis) {
        return OpUtils.tryGet(() -> lock(resource), (e) -> e, timeoutMillis, tryGetLockIntervalMillis);
    }

    @Override
    public boolean unlock(String resource) {
        Objects.requireNonNull(resource);

        Long tenantId = (tenantId = UserContextHolder.getTenantId()) == null ? 0L : tenantId;

        if(null != disLockId) {
            disLockDao.deleteDisLock(disLockId);
        } else {
            DisLockPO found = disLockDao.getDisLock(tenantId, resource);
            if(found != null) {
                disLockDao.deleteDisLock(found.getDisLockId());
            }
        }
        return true;
    }

}
